home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume21 / mmv / part02 < prev    next >
Encoding:
Internet Message Format  |  1990-04-08  |  52.6 KB

  1. Subject:  v21i088:  Safely rename wildcarded files, Part02/02
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: fbb8a4a2 cf48eba6 238c816b ae51c752
  5.  
  6. Submitted-by: Vladimir Lanin <lanin@csd4.cs.nyu.edu>
  7. Posting-number: Volume 21, Issue 88
  8. Archive-name: mmv/part02
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 2 (of 2)."
  17. # Contents:  mmv.c.1
  18. # Wrapped by rsalz@litchi.bbn.com on Mon Apr  9 17:05:23 1990
  19. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  20. if test -f 'mmv.c.1' -a "${1}" != "-c" ; then 
  21.   echo shar: Will not clobber existing file \"'mmv.c.1'\"
  22. else
  23. echo shar: Extracting \"'mmv.c.1'\" \(49841 characters\)
  24. sed "s/^X//" >'mmv.c.1' <<'END_OF_FILE'
  25. X/*
  26. X    mmv 1.0
  27. X    Copyright (c) 1990 Vladimir Lanin.
  28. X    This program may be freely used and copied on a non-commercial basis.
  29. X    The author assumes no responsibility for any damage or data loss that may
  30. X    result from the use of this program.
  31. X
  32. X    Author may be reached at:
  33. X
  34. X    lanin@csd4.nyu.edu
  35. X
  36. X    Vladimir Lanin
  37. X    330 Wadsworth Ave, Apt 6F,
  38. X    New York, NY 10040
  39. X*/
  40. X
  41. X/*
  42. X    Define SYSV to compile under System V.
  43. X    If your System V has a rename() call, define RENAME.
  44. X    Otherwise, mmv will only be able to rename directories (via option -r)
  45. X    when running as the super-user.
  46. X    There is no reason to set the suid bit on mmv if rename() is available.
  47. X    It is important that mmv not be run with effective uid set
  48. X    to any value other than either the real uid or the super-user.
  49. X    Even when running with effective uid set to super-user,
  50. X    mmv will only perform actions permitted to the real uid.
  51. X
  52. X    Define MSDOS to compile under MS-D*S Turbo C 1.5.
  53. X    If you prefer mmv's output to use /'s instead of \'s under MS-D*S,
  54. X    define SLASH.
  55. X
  56. X    When neither MSDOS nor SYSV are defined, compiles under BSD.
  57. X
  58. X    RENAME is automatically defined under MSDOS and BSD.
  59. X
  60. X    If you are running a (UN*X) system that provides the
  61. X    "struct dirent" readdir() directory reading standard,
  62. X    define DIRENT. Otherwise, mmv uses the BSD-like
  63. X    "struct direct" readdir().
  64. X    If your (UN*X) system has neither of these, get the "dirent"
  65. X    by Doug Gwyn, available as gwyn-dir-lib in volume 9
  66. X    of the comp.sources.unix archives.
  67. X*/
  68. X
  69. Xstatic char USAGE[] =
  70. X#ifdef MSDOS
  71. X
  72. X"Usage: \
  73. X%s [-m|x%s|c|o|a|z] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\
  74. X\n\
  75. XUse =N in the ``to'' pattern to get the string matched\n\
  76. Xby the N'th ``from'' pattern wildcard.\n";
  77. X
  78. X#define OTHEROPT (_osmajor < 3 ? "" : "|r")
  79. X
  80. X#else
  81. X
  82. X"Usage: \
  83. X%s [-m|x|r|c|o|a|l%s] [-h] [-d|p] [-g|t] [-v|n] [from to]\n\
  84. X\n\
  85. XUse =[l|u]N in the ``to'' pattern to get the [lowercase|uppercase of the]\n\
  86. Xstring matched by the N'th ``from'' pattern wildcard.\n\
  87. X\n\
  88. XA ``from'' pattern containing wildcards should be quoted when given\n\
  89. Xon the command line.\n";
  90. X
  91. X#ifdef SYSV
  92. X#define OTHEROPT ""
  93. X#else
  94. X#define OTHEROPT "|s"
  95. X#endif
  96. X
  97. X#endif
  98. X
  99. X#include <stdio.h>
  100. X#include <ctype.h>
  101. X#include <string.h>
  102. X
  103. X#ifdef MSDOS
  104. X/* for MS-DOS (under Turbo C 1.5)*/
  105. X
  106. X#include <stdlib.h>
  107. X#include <sys/stat.h>
  108. X#include <dos.h>
  109. X#include <dir.h>
  110. X#include <io.h>
  111. X#include <fcntl.h>
  112. X
  113. X#define ESC '\''
  114. X#ifdef SLASH
  115. X#define SLASH '/'
  116. X#define OTHERSLASH '\\'
  117. X#else
  118. X#define SLASH '\\'
  119. X#define OTHERSLASH '/'
  120. X#endif
  121. X
  122. Xtypedef int DIRID;
  123. Xtypedef int DEVID;
  124. X
  125. Xstatic char TTY[] = "/dev/con";
  126. Xextern unsigned _stklen = 10000;
  127. X
  128. X#define RENAME
  129. X
  130. X#else
  131. X/* for various flavors of UN*X */
  132. X
  133. X#include <sys/types.h>
  134. X#include <sys/stat.h>
  135. X#include <sys/file.h>
  136. X#include <sys/signal.h>
  137. X#include <fcntl.h>
  138. Xextern char *getenv();
  139. Xextern long lseek();
  140. Xextern char *malloc();
  141. X
  142. X#ifdef DIRENT
  143. X#include <dirent.h>
  144. Xtypedef struct dirent DIRENTRY;
  145. X#else
  146. X#ifdef SYSV
  147. X#include <sys/dir.h>
  148. X/* might need to be changed to <dir.h> */
  149. X#else
  150. X#include <sys/dir.h>
  151. X#endif
  152. Xtypedef struct direct DIRENTRY;
  153. X#endif
  154. X
  155. X#define void char    /* might want to remove this line */
  156. X
  157. X#ifndef O_BINARY
  158. X#define O_BINARY 0
  159. X#endif
  160. X#ifndef R_OK
  161. X#define R_OK 4
  162. X#define W_OK 2
  163. X#define X_OK 1
  164. X#endif
  165. X
  166. X#define ESC '\\'
  167. X#define SLASH '/'
  168. X
  169. Xtypedef ino_t DIRID;
  170. Xtypedef dev_t DEVID;
  171. X
  172. X#define MAXPATH 1024
  173. X
  174. Xstatic char TTY[] = "/dev/tty";
  175. X
  176. X#ifdef SYSV
  177. X/* for System V */
  178. X
  179. Xstruct utimbuf {
  180. X    time_t actime;
  181. X    time_t modtime;
  182. X};
  183. X#define utimes(f, t) utime((f), (t))
  184. X
  185. X
  186. X#else
  187. X/* for BSD */
  188. X
  189. X#define RENAME
  190. X
  191. X#include <sys/time.h>
  192. X
  193. X#endif
  194. X
  195. X#endif
  196. X
  197. X
  198. X#define mylower(c) (isupper(c) ? (c)-'A'+'a' : (c))
  199. X#define myupper(c) (islower(c) ? (c)-'a'+'A' : (c))
  200. X#define STRLEN(s) (sizeof(s) - 1)
  201. X#define mydup(s) (strcpy((char *)challoc(strlen(s) + 1, 0), (s)))
  202. X
  203. X
  204. X#define DFLT 0x001
  205. X#define NORMCOPY 0x002
  206. X#define OVERWRITE 0x004
  207. X#define NORMMOVE 0x008
  208. X#define XMOVE 0x010
  209. X#define DIRMOVE 0x020
  210. X#define NORMAPPEND 0x040
  211. X#define ZAPPEND 0x080
  212. X#define HARDLINK 0x100
  213. X#define SYMLINK 0x200
  214. X
  215. X#define COPY (NORMCOPY | OVERWRITE)
  216. X#define MOVE (NORMMOVE | XMOVE | DIRMOVE)
  217. X#define APPEND (NORMAPPEND | ZAPPEND)
  218. X#define LINK (HARDLINK | SYMLINK)
  219. X
  220. Xstatic char MOVENAME[] = "mmv";
  221. Xstatic char COPYNAME[] = "mcp";
  222. Xstatic char APPENDNAME[] = "mad";
  223. Xstatic char LINKNAME[] = "mln";
  224. X
  225. X#define ASKDEL 0
  226. X#define ALLDEL 1
  227. X#define NODEL 2
  228. X
  229. X#define ASKBAD 0
  230. X#define SKIPBAD 1
  231. X#define ABORTBAD 2
  232. X
  233. X#define STAY 0
  234. X#define LOWER 1
  235. X#define UPPER 2
  236. X
  237. X#define MAXWILD 20
  238. X#define MAXPATLEN MAXPATH
  239. X#define INITROOM 10
  240. X#define CHUNKSIZE 2048
  241. X#define BUFSIZE 4096
  242. X
  243. X#define FI_STTAKEN 0x01
  244. X#define FI_LINKERR 0x02
  245. X#define FI_INSTICKY 0x04
  246. X#define FI_NODEL 0x08
  247. X#define FI_KNOWWRITE 0x010
  248. X#define FI_CANWRITE 0x20
  249. X#define FI_ISDIR 0x40
  250. X#define FI_ISLNK 0x80
  251. X
  252. Xtypedef struct {
  253. X    char *fi_name;
  254. X    struct rep *fi_rep;
  255. X#ifdef MSDOS
  256. X    char fi_attrib;
  257. X#else
  258. X    short fi_mode;
  259. X    char fi_stflags;
  260. X#endif
  261. X} FILEINFO;
  262. X
  263. X#define DI_KNOWWRITE 0x01
  264. X#define DI_CANWRITE 0x02
  265. X#define DI_CLEANED 0x04
  266. X
  267. Xtypedef struct {
  268. X    DEVID di_vid;
  269. X    DIRID di_did;
  270. X    unsigned di_nfils;
  271. X    FILEINFO **di_fils;
  272. X    char di_flags;
  273. X} DIRINFO;
  274. X
  275. X#define H_NODIR 1
  276. X#define H_NOREADDIR 2
  277. X
  278. Xtypedef struct {
  279. X    char *h_name;
  280. X    DIRINFO *h_di;
  281. X    char h_err;
  282. X} HANDLE;
  283. X
  284. X#define R_ISX 0x01
  285. X#define R_SKIP 0x02
  286. X#define R_DELOK 0x04
  287. X#define R_ISALIASED 0x08
  288. X#define R_ISCYCLE 0x10
  289. X#define R_ONEDIRLINK 0x20
  290. X
  291. Xtypedef struct rep {
  292. X    HANDLE *r_hfrom;
  293. X    FILEINFO *r_ffrom;
  294. X    HANDLE *r_hto;
  295. X    char *r_nto;            /* non-path part of new name */
  296. X    FILEINFO *r_fdel;
  297. X    struct rep *r_first;
  298. X    struct rep *r_thendo;
  299. X    struct rep *r_next;
  300. X    char r_flags;
  301. X} REP;
  302. X
  303. Xtypedef struct {
  304. X    REP *rd_p;
  305. X    DIRINFO *rd_dto;
  306. X    char *rd_nto;
  307. X    unsigned rd_i;
  308. X} REPDICT;
  309. X
  310. Xtypedef struct chunk {
  311. X    struct chunk *ch_next;
  312. X    unsigned ch_len;
  313. X} CHUNK;
  314. X
  315. Xtypedef struct {
  316. X    CHUNK *sl_first;
  317. X    char *sl_unused;
  318. X    int sl_len;
  319. X} SLICER;
  320. X
  321. X
  322. Xstatic void init(/* */);
  323. Xstatic void procargs(/* int argc, char **argv,
  324. X    char **pfrompat, char **ptopat */);
  325. Xstatic void matchpats(/* char *cfrom, char *cto */);
  326. Xstatic int getpat(/* */);
  327. Xstatic int getword(/* char *buf */);
  328. Xstatic void matchpat(/*  */);
  329. Xstatic int parsepat(/*  */);
  330. Xstatic int dostage(/* char *lastend, char *pathend,
  331. X    char **start1, int *len1, int stage, int anylev */);
  332. Xstatic int trymatch(/* FILEINFO *ffrom, char *pat */);
  333. Xstatic int keepmatch(/* FILEINFO *ffrom, char *pathend,
  334. X    int *pk, int needslash, int dirs, int fils */);
  335. Xstatic int badrep(/* HANDLE *hfrom, FILEINFO *ffrom,
  336. X    HANDLE **phto, char **pnto, FILEINFO **pfdel, int *pflags */);
  337. Xstatic int checkto(/* HANDLE *hfrom, char *f,
  338. X    HANDLE **phto, char **pnto, FILEINFO **pfdel */);
  339. Xstatic char *getpath(/* char *tpath */);
  340. Xstatic int badname(/* char *s */);
  341. Xstatic FILEINFO *fsearch(/* char *s, DIRINFO *d */);
  342. Xstatic int ffirst(/* char *s, int n, DIRINFO *d */);
  343. Xstatic HANDLE *checkdir(/* char *p, char *pathend, int which */);
  344. Xstatic void takedir(/*
  345. X    char *p, DIRINFO *di, int sticky
  346. Xor
  347. X    struct ffblk *pff, DIRINFO *di
  348. X*/);
  349. Xstatic int fcmp(/* FILEINFO **pf1, FILEINFO **pf2 */);
  350. Xstatic HANDLE *hadd(/* char *n */);
  351. Xstatic int hsearch(/* char *n, int which, HANDLE **ph */);
  352. Xstatic DIRINFO *dadd(/* DEVID v, DIRID d */);
  353. Xstatic DIRINFO *dsearch(/* DEVID v, DIRID d */);
  354. Xstatic int match(/* char *pat, char *s, char **start1, int *len1 */);
  355. Xstatic void makerep(/*  */);
  356. Xstatic void checkcollisions(/*  */);
  357. Xstatic int rdcmp(/* REPDICT *rd1, REPDICT *rd2 */);
  358. Xstatic void findorder(/*  */);
  359. Xstatic void scandeletes(/* int (*pkilldel)(REP *p) */);
  360. Xstatic int baddel(/* REP *p */);
  361. Xstatic int skipdel(/* REP *p */);
  362. Xstatic void nochains(/*  */);
  363. Xstatic void printchain(/* REP *p */);
  364. Xstatic void goonordie(/*  */);
  365. Xstatic void doreps(/*  */);
  366. Xstatic long appendalias(/* REP *first, REP *p, int *pprintaliased */);
  367. Xstatic int movealias(/* REP *first, REP *p, int *pprintaliased */);
  368. Xstatic int snap(/* REP *first, REP *p */);
  369. Xstatic void showdone(/* REP *fin */);
  370. Xstatic void breakout(/*  */);
  371. Xstatic int breakrep(/* */);
  372. Xstatic void breakstat(/* */);
  373. Xstatic void quit(/*  */);
  374. Xstatic int copymove(/* REP *p */);
  375. Xstatic int copy(/* FILENFO *f, long len */);
  376. Xstatic int myunlink(/* char *n, FILEINFO *f */);
  377. Xstatic int getreply(/* char *m, int failact */);
  378. Xstatic void *myalloc(/* unsigned k */);
  379. Xstatic void *challoc(/* int k, int which */);
  380. Xstatic void chgive(/* void *p, unsigned k */);
  381. Xstatic int mygetc(/* */);
  382. X#ifdef MSDOS
  383. Xstatic int leave(/*  */);
  384. Xstatic void cleanup(/*  */);
  385. X#else
  386. Xstatic int getstat(/* char *full, FILEINFO *f */);
  387. Xstatic int dwritable(/* HANDLE *h */);
  388. Xstatic int fwritable(/* char *hname, FILEINFO *f */);
  389. Xstatic void memmove(/* void *to, void *from, int k */);
  390. X#endif
  391. X#ifndef RENAME
  392. Xstatic int rename(/* char *from, char *to */);
  393. X#endif
  394. X
  395. Xstatic int op, badstyle, delstyle, verbose, noex, matchall;
  396. Xstatic int patflags;
  397. X
  398. Xstatic unsigned ndirs = 0, dirroom;
  399. Xstatic DIRINFO **dirs;
  400. Xstatic unsigned nhandles = 0, handleroom;
  401. Xstatic HANDLE **handles;
  402. Xstatic HANDLE badhandle = {"\200", NULL, 0};
  403. Xstatic HANDLE *(lasthandle[2]) = {&badhandle, &badhandle};
  404. Xstatic unsigned nreps = 0;
  405. Xstatic REP hrep, *lastrep = &hrep;
  406. Xstatic CHUNK *freechunks = NULL;
  407. Xstatic SLICER slicer[2] = {{NULL, NULL, 0}, {NULL, NULL, 0}};
  408. X
  409. Xstatic int badreps = 0, paterr = 0, direrr, failed = 0, gotsig = 0, repbad;
  410. Xstatic FILE *outfile = stdout;
  411. X
  412. Xstatic char IDF[] = "$$mmvdid.";
  413. Xstatic char TEMP[] = "$$mmvtmp.";
  414. Xstatic char TOOLONG[] = "(too long)";
  415. Xstatic char EMPTY[] = "(empty)";
  416. X
  417. Xstatic char SLASHSTR[] = {SLASH, '\0'};
  418. X
  419. Xstatic char PATLONG[] = "%.40s... : pattern too long.\n";
  420. X
  421. Xstatic char from[MAXPATLEN], to[MAXPATLEN];
  422. Xstatic int fromlen, tolen;
  423. Xstatic char *(stagel[MAXWILD]), *(firstwild[MAXWILD]), *(stager[MAXWILD]);
  424. Xstatic int nwilds[MAXWILD];
  425. Xstatic int nstages;
  426. Xstatic char pathbuf[MAXPATH];
  427. Xstatic char fullrep[MAXPATH + 1];
  428. Xstatic char *(start[MAXWILD]);
  429. Xstatic int len[MAXWILD];
  430. Xstatic char hasdot[MAXWILD];
  431. Xstatic REP mistake;
  432. X#define MISTAKE (&mistake)
  433. X
  434. X#ifdef MSDOS
  435. X
  436. Xstatic int olddevflag, curdisk, maxdisk;
  437. Xstatic struct {
  438. X    char ph_banner[30];
  439. X    char ph_name[9];
  440. X    int ph_dfltop;
  441. X    int ph_safeid;
  442. X    int ph_clustoff;
  443. X    int ph_driveoff;
  444. X    int ph_drivea;
  445. X} patch = {"mmv 1.0 patchable flags", "mmv", XMOVE, 1, 0};
  446. X
  447. X#define DFLTOP (patch.ph_dfltop)
  448. X#define CLUSTNO(pff) (*(int *)(((char *)(pff)) + patch.ph_clustoff))
  449. X#define DRIVENO(pff) (*(((char *)(pff)) + patch.ph_driveoff) - patch.ph_drivea)
  450. X
  451. X
  452. X#else
  453. X
  454. X#define DFLTOP XMOVE
  455. X
  456. Xstatic char *home;
  457. Xstatic int homelen;
  458. Xstatic int uid, euid, oldumask;
  459. Xstatic DIRID cwdd = -1;
  460. Xstatic DEVID cwdv = -1;
  461. X
  462. X#endif
  463. X
  464. X
  465. Xint main(argc, argv)
  466. X    int argc;
  467. X    char *(argv[]);
  468. X{
  469. X    char *frompat, *topat;
  470. X
  471. X    init();
  472. X    procargs(argc, argv, &frompat, &topat);
  473. X    matchpats(frompat, topat);
  474. X    if (!(op & APPEND))
  475. X        checkcollisions();
  476. X    findorder();
  477. X    if (op & (COPY | LINK))
  478. X        nochains();
  479. X    scandeletes(baddel);
  480. X    goonordie();
  481. X    if (!(op & APPEND) && delstyle == ASKDEL)
  482. X        scandeletes(skipdel);
  483. X    doreps();
  484. X    return(failed ? 2 : nreps == 0 && (paterr || badreps));
  485. X}
  486. X
  487. X
  488. Xstatic void init()
  489. X{
  490. X#ifdef MSDOS
  491. X    curdisk = getdisk();
  492. X    maxdisk = setdisk(curdisk);
  493. X/*
  494. X    Read device availability : undocumented internal MS-DOS function.
  495. X    If (_DX == 0) then \dev\ must precede device names.
  496. X*/
  497. X    bdos(0x37, 0, 2);
  498. X    olddevflag = _DX;
  499. X/*
  500. X    Write device availability: undocumented internal MS-DOS function.
  501. X    Specify \dev\ must precede device names.
  502. X*/
  503. X    bdos(0x37, 0, 3);
  504. X    atexit((atexit_t)cleanup);
  505. X    ctrlbrk((int (*)())breakout);
  506. X#else
  507. X    struct stat dstat;
  508. X
  509. X    if ((home = getenv("HOME")) == NULL || strcmp(home, SLASHSTR) == 0)
  510. X        home = "";
  511. X    if (!stat(".", &dstat)) {
  512. X        cwdd = dstat.st_ino;
  513. X        cwdv = dstat.st_dev;
  514. X    }
  515. X    oldumask = umask(0);
  516. X    euid = geteuid();
  517. X    uid = getuid();
  518. X    signal(SIGINT, breakout);
  519. X#endif
  520. X
  521. X    dirroom = handleroom = INITROOM;
  522. X    dirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
  523. X    handles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
  524. X    ndirs = nhandles = 0;
  525. X}
  526. X
  527. X
  528. Xstatic void procargs(argc, argv, pfrompat, ptopat)
  529. X    int argc;
  530. X    char **argv;
  531. X    char **pfrompat, **ptopat;
  532. X{
  533. X    char *p, c;
  534. X    char *cmdname = argv[0];
  535. X
  536. X#ifdef MSDOS
  537. X#define CMDNAME (patch.ph_name)
  538. X#else
  539. X#define CMDNAME cmdname
  540. X#endif
  541. X
  542. X    op = DFLT;
  543. X    verbose = noex = matchall = 0;
  544. X    delstyle = ASKDEL;
  545. X    badstyle = ASKBAD;
  546. X    for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++)
  547. X        for (p = *argv + 1; *p != '\0'; p++) {
  548. X            c = mylower(*p);
  549. X            if (c == 'v' && !noex)
  550. X                verbose = 1;
  551. X            else if (c == 'n' && !verbose)
  552. X                noex = 1;
  553. X            else if (c == 'h')
  554. X                matchall = 1;
  555. X            else if (c == 'd' && delstyle == ASKDEL)
  556. X                delstyle = ALLDEL;
  557. X            else if (c == 'p' && delstyle == ASKDEL)
  558. X                delstyle = NODEL;
  559. X            else if (c == 'g' && badstyle == ASKBAD)
  560. X                badstyle = SKIPBAD;
  561. X            else if (c == 't' && badstyle == ASKBAD)
  562. X                badstyle = ABORTBAD;
  563. X            else if (c == 'm' && op == DFLT)
  564. X                op = NORMMOVE;
  565. X            else if (c == 'x' && op == DFLT)
  566. X                op = XMOVE;
  567. X            else if (c == 'r' && op == DFLT)
  568. X                op = DIRMOVE;
  569. X            else if (c == 'c' && op == DFLT)
  570. X                op = NORMCOPY;
  571. X            else if (c == 'o' && op == DFLT)
  572. X                op = OVERWRITE;
  573. X            else if (c == 'a' && op == DFLT)
  574. X                op = NORMAPPEND;
  575. X#ifdef MSDOS
  576. X            else if (c == 'z' && op == DFLT)
  577. X                op = ZAPPEND;
  578. X#else
  579. X            else if (c == 'l' && op == DFLT)
  580. X                op = HARDLINK;
  581. X#ifndef SYSV
  582. X            else if (c == 's' && op == DFLT)
  583. X                op = SYMLINK;
  584. X#endif
  585. X#endif
  586. X            else {
  587. X                fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
  588. X                exit(1);
  589. X            }
  590. X        }
  591. X
  592. X    if (op == DFLT)
  593. X        if (strcmp(cmdname, MOVENAME) == 0)
  594. X            op = XMOVE;
  595. X        else if (strcmp(cmdname, COPYNAME) == 0)
  596. X            op = NORMCOPY;
  597. X        else if (strcmp(cmdname, APPENDNAME) == 0)
  598. X            op = NORMAPPEND;
  599. X        else if (strcmp(cmdname, LINKNAME) == 0)
  600. X            op = HARDLINK;
  601. X        else
  602. X            op = DFLTOP;
  603. X    if (
  604. X        op & DIRMOVE &&
  605. X#ifdef MSDOS
  606. X        _osmajor < 3
  607. X#else
  608. X#ifndef RENAME
  609. X        euid != 0
  610. X#else
  611. X        0
  612. X#endif
  613. X#endif
  614. X    ) {
  615. X        fprintf(stderr,
  616. X            "Unable to do directory renames. Option -r refused.\n");
  617. X        quit();
  618. X    }
  619. X
  620. X#ifndef MSDOS
  621. X    if (euid != uid && !(op & DIRMOVE)) {
  622. X        setuid(uid);
  623. X        setgid(getgid());
  624. X    }
  625. X#endif
  626. X
  627. X    if (badstyle != ASKBAD && delstyle == ASKDEL)
  628. X        delstyle = NODEL;
  629. X
  630. X    if (argc == 0)
  631. X        *pfrompat = NULL;
  632. X    else if (argc == 2) {
  633. X        *pfrompat = *(argv++);
  634. X        *ptopat = *(argv++);
  635. X    }
  636. X    else {
  637. X        fprintf(stderr, USAGE, CMDNAME, OTHEROPT);
  638. X        exit(1);
  639. X    }
  640. X}
  641. X
  642. X
  643. Xstatic void matchpats(cfrom, cto)
  644. X    char *cfrom, *cto;
  645. X{
  646. X    if (cfrom == NULL)
  647. X        while (getpat())
  648. X            matchpat();
  649. X    else if ((fromlen = strlen(cfrom)) >= MAXPATLEN) {
  650. X        printf(PATLONG, cfrom);
  651. X        paterr = 1;
  652. X    }
  653. X    else if ((tolen = strlen(cto)) >= MAXPATLEN) {
  654. X        printf(PATLONG, cto);
  655. X        paterr = 1;
  656. X    }
  657. X    else {
  658. X        strcpy(from, cfrom);
  659. X        strcpy(to, cto);
  660. X        matchpat();
  661. X    }
  662. X}
  663. X
  664. X
  665. Xstatic int getpat()
  666. X{
  667. X    int c, gotit = 0;
  668. X    char extra[MAXPATLEN];
  669. X
  670. X    patflags = 0;
  671. X    do {
  672. X        if ((fromlen = getword(from)) == 0 || fromlen == -1)
  673. X            goto nextline;
  674. X
  675. X        do {
  676. X            if ((tolen = getword(to)) == 0) {
  677. X                printf("%s -> ? : missing replacement pattern.\n", from);
  678. X                goto nextline;
  679. X            }
  680. X            if (tolen == -1)
  681. X                goto nextline;
  682. X        } while (
  683. X            tolen == 2 &&
  684. X            (to[0] == '-' || to[0] == '=') &&
  685. X            (to[1] == '>' || to[1] == '^')
  686. X        );
  687. X        if (getword(extra) == 0)
  688. X            gotit = 1;
  689. X        else if (strcmp(extra, "(*)") == 0) {
  690. X            patflags |= R_DELOK;
  691. X            gotit = (getword(extra) == 0);
  692. X        }
  693. X
  694. Xnextline:
  695. X        while ((c = mygetc()) != '\n' && c != EOF)
  696. X            ;
  697. X        if (c == EOF)
  698. X            return(0);
  699. X    } while (!gotit);
  700. X
  701. X    return(1);
  702. X}
  703. X
  704. X
  705. Xstatic int getword(buf)
  706. X    char *buf;
  707. X{
  708. X    int c, prevc, n;
  709. X    char *p;
  710. X
  711. X    p = buf;
  712. X    prevc = ' ';
  713. X    n = 0;
  714. X    while ((c = mygetc()) != EOF && (prevc == ESC || !isspace(c))) {
  715. X        if (n == -1)
  716. X            continue;
  717. X        if (n == MAXPATLEN - 1) {
  718. X            *p = '\0';
  719. X            printf(PATLONG, buf);
  720. X            n = -1;
  721. X        }
  722. X        *(p++) = c;
  723. X        n++;
  724. X        prevc = c;
  725. X    }
  726. X    *p = '\0';
  727. X    while (c != EOF && isspace(c) && c != '\n')
  728. X        c = mygetc();
  729. X    if (c != EOF)
  730. X        ungetc(c, stdin);
  731. X    return(n);
  732. X}
  733. X
  734. X
  735. Xstatic void matchpat()
  736. X{
  737. X    if (parsepat())
  738. X        paterr = 1;
  739. X    else if (dostage(from, pathbuf, start, len, 0, 0)) {
  740. X        printf("%s -> %s : no match.\n", from, to);
  741. X        paterr = 1;
  742. X    }
  743. X}
  744. X
  745. X
  746. Xstatic int parsepat()
  747. X{
  748. X    char *p, *lastname, c;
  749. X    int totwilds, instage, x, havedot;
  750. X    static char TRAILESC[] = "%s -> %s : trailing %c is superfluous.\n";
  751. X
  752. X    lastname = from;
  753. X#ifdef MSDOS
  754. X    havedot = 0;
  755. X    if (from[0] != '\0' && from[1] == ':')
  756. X        lastname += 2;
  757. X#else
  758. X    if (from[0] == '~' && from[1] == SLASH) {
  759. X        if ((homelen = strlen(home)) + fromlen > MAXPATLEN) {
  760. X            printf(PATLONG, from);
  761. X            return(-1);
  762. X        }
  763. X        memmove(from + homelen, from + 1, fromlen);
  764. X        memmove(from, home, homelen);
  765. X        lastname += homelen + 1;
  766. X    }
  767. X#endif
  768. X    totwilds = nstages = instage = 0;
  769. X    for (p = lastname; (c = *p) != '\0'; p++)
  770. X        switch (c) {
  771. X#ifdef MSDOS
  772. X        case '.':
  773. X            havedot = 1;
  774. X            break;
  775. X        case OTHERSLASH:
  776. X            *p = SLASH;
  777. X#endif
  778. X         case SLASH:
  779. X#ifdef MSDOS
  780. X            if (!havedot && lastname != p) {
  781. X                if (fromlen++ == MAXPATLEN) {
  782. X                    printf(PATLONG, from);
  783. X                    return(-1);
  784. X                }
  785. X                memmove(p + 1, p, strlen(p) + 1);
  786. X                *(p++) = '.';
  787. X            }
  788. X            else
  789. X                havedot = 0;
  790. X#endif
  791. X            lastname = p + 1;
  792. X            if (instage) {
  793. X                if (firstwild[nstages] == NULL)
  794. X                    firstwild[nstages] = p;
  795. X                stager[nstages++] = p;
  796. X                instage = 0;
  797. X            }
  798. X            break;
  799. X        case ';':
  800. X            if (lastname != p) {
  801. X                printf("%s -> %s : badly placed ;.\n", from, to);
  802. X                return(-1);
  803. X            }
  804. X        case '!':
  805. X        case '*':
  806. X        case '?':
  807. X        case '[':
  808. X#ifdef MSDOS
  809. X            if ((hasdot[totwilds] = (c == '!')) != 0)
  810. X                havedot = 1;
  811. X#endif
  812. X            if (totwilds++ == MAXWILD) {
  813. X                printf("%s -> %s : too many wildcards.\n", from, to);
  814. X                return(-1);
  815. X            }
  816. X            if (instage) {
  817. X                nwilds[nstages]++;
  818. X                if (firstwild[nstages] == NULL)
  819. X                    firstwild[nstages] = p;
  820. X            }
  821. X            else {
  822. X                stagel[nstages] = lastname;
  823. X                firstwild[nstages] = (c == ';' ? NULL : p);
  824. X                nwilds[nstages] = 1;
  825. X                instage = 1;
  826. X            }
  827. X            if (c != '[')
  828. X                break;
  829. X            while ((c = *(++p)) != ']') {
  830. X                switch (c) {
  831. X                case '\0':
  832. X                    printf("%s -> %s : missing ].\n", from, to);
  833. X                    return(-1);
  834. X#ifdef MSDOS
  835. X                case '.':
  836. X                case ':':
  837. X                case OTHERSLASH:
  838. X#endif
  839. X                case SLASH:
  840. X                    printf("%s -> %s : '%c' can not be part of [].\n",
  841. X                        from, to, c);
  842. X                    return(-1);
  843. X                case ESC:
  844. X                    if ((c = *(++p)) == '\0') {
  845. X                        printf(TRAILESC, from, to, ESC);
  846. X                        return(-1);
  847. X                    }
  848. X#ifdef MSDOS
  849. X                default:
  850. X                    if (isupper(c))
  851. X                        *p = c + ('a' - 'A');
  852. X#endif
  853. X                }
  854. X            }
  855. X            break;
  856. X        case ESC:
  857. X            if ((c = *(++p)) == '\0') {
  858. X                printf(TRAILESC, from, to, ESC);
  859. X                return(-1);
  860. X            }
  861. X#ifdef MSDOS
  862. X        default:
  863. X            if (isupper(c))
  864. X                *p = c + ('a' - 'A');
  865. X#endif
  866. X        }
  867. X
  868. X#ifdef MSDOS
  869. X    if (!havedot && lastname != p) {
  870. X        if (fromlen++ == MAXPATLEN) {
  871. X            printf(PATLONG, from);
  872. X            return(-1);
  873. X        }
  874. X        strcpy(p++, ".");
  875. X    }
  876. X#endif
  877. X
  878. X    if (instage) {
  879. X        if (firstwild[nstages] == NULL)
  880. X            firstwild[nstages] = p;
  881. X        stager[nstages++] = p;
  882. X    }
  883. X    else {
  884. X        stagel[nstages] = lastname;
  885. X        nwilds[nstages] = 0;
  886. X        firstwild[nstages] = p;
  887. X        stager[nstages++] = p;
  888. X    }
  889. X
  890. X    lastname = to;
  891. X#ifdef MSDOS
  892. X    havedot = 0;
  893. X    if (to[0] != '\0' && to[1] == ':')
  894. X        lastname += 2;
  895. X#else
  896. X    if (to[0] == '~' && to[1] == SLASH) {
  897. X        if ((homelen = strlen(home)) + tolen > MAXPATLEN) {
  898. X            printf(PATLONG, to);
  899. X                return(-1);
  900. X        }
  901. X        memmove(to + homelen, to + 1, tolen);
  902. X        memmove(to, home, homelen);
  903. X        lastname += homelen + 1;
  904. X    }
  905. X#endif
  906. X
  907. X    for (p = lastname; (c = *p) != '\0'; p++)
  908. X        switch (c) {
  909. X#ifdef MSDOS
  910. X        case '.':
  911. X            havedot = 1;
  912. X            break;
  913. X        case OTHERSLASH:
  914. X            *p = SLASH;
  915. X#endif
  916. X        case SLASH:
  917. X            if (op & DIRMOVE) {
  918. X                printf("%s -> %s : no path allowed in target under -r.\n",
  919. X                    from, to);
  920. X                return(-1);
  921. X            }
  922. X#ifdef MSDOS
  923. X            if (!havedot && lastname != p) {
  924. X                if (tolen++ == MAXPATLEN) {
  925. X                    printf(PATLONG, to);
  926. X                    return(-1);
  927. X                }
  928. X                memmove(p + 1, p, strlen(p) + 1);
  929. X                *(p++) = '.';
  930. X            }
  931. X            else
  932. X                havedot = 0;
  933. X#endif
  934. X            lastname = p + 1;
  935. X            break;
  936. X        case '=':
  937. X            c = *(++p);
  938. X            if (c == 'l' || c == 'u') {
  939. X#ifdef MSDOS
  940. X                strcpy(p, p + 1);
  941. X                c = *p;
  942. X#else
  943. X                c = *(++p);
  944. X#endif
  945. X            }
  946. X            if (!isdigit(c)) {
  947. X                printf("%s -> %s : expected digit (not '%c') after =.\n",
  948. X                    from, to, c);
  949. X                return(-1);
  950. X            }
  951. X            for(x = 0; ;x *= 10) {
  952. X                x += c - '0';
  953. X                c = *(p+1);
  954. X                if (!isdigit(c))
  955. X                    break;
  956. X                p++;
  957. X            }
  958. X            if (x < 1 || x > totwilds) {
  959. X                printf("%s -> %s : wildcard %d does not exist.\n",
  960. X                    from, to, x);
  961. X                return(-1);
  962. X            }
  963. X#ifdef MSDOS
  964. X            if (hasdot[x - 1])
  965. X                havedot = 1;
  966. X#endif
  967. X            break;
  968. X        case ESC:
  969. X            if ((c = *(++p)) == '\0') {
  970. X                printf(TRAILESC, from, to, ESC);
  971. X                return(-1);
  972. X            }
  973. X        default:
  974. X            if (
  975. X#ifdef MSDOS
  976. X                c <= ' ' || c >= 127 ||
  977. X                strchr(":/\\*?[]=+;,\"|<>", c) != NULL
  978. X#else
  979. X                c & 0x80
  980. X#endif
  981. X            ) {
  982. X                printf("%s -> %s : illegal character '%c' (0x%02X).\n",
  983. X                    from, to, c, c);
  984. X                return(-1);
  985. X            }
  986. X#ifdef MSDOS
  987. X            if (isupper(c))
  988. X                *p = c + ('a' - 'A');
  989. X#endif
  990. X        }
  991. X
  992. X#ifdef MSDOS
  993. X    if (!havedot && lastname != p) {
  994. X        if (tolen++ == MAXPATLEN) {
  995. X            printf(PATLONG, to);
  996. X            return(-1);
  997. X        }
  998. X        strcpy(p++, ".");
  999. X    }
  1000. X#endif
  1001. X
  1002. X    return(0);
  1003. X}
  1004. X
  1005. X
  1006. Xstatic int dostage(lastend, pathend, start1, len1, stage, anylev)
  1007. X    char *lastend, *pathend;
  1008. X    char **start1;
  1009. X    int *len1;
  1010. X    int stage;
  1011. X    int anylev;
  1012. X{
  1013. X    DIRINFO *di;
  1014. X    HANDLE *h, *hto;
  1015. X    int prelen, litlen, nfils, i, k, flags, try;
  1016. X    FILEINFO **pf, *fdel;
  1017. X    char *nto, *firstesc;
  1018. X    REP *p;
  1019. X    int wantdirs, ret = 1, laststage = (stage + 1 == nstages);
  1020. X
  1021. X    wantdirs = !laststage ||
  1022. X        (op & (DIRMOVE | SYMLINK)) ||
  1023. X        (nwilds[nstages - 1] == 0);
  1024. X
  1025. X    if (!anylev) {
  1026. X        prelen = stagel[stage] - lastend;
  1027. X        if (pathend - pathbuf + prelen >= MAXPATH) {
  1028. X            printf("%s -> %s : search path after %s too long.\n",
  1029. X                from, to, pathbuf);
  1030. X            paterr = 1;
  1031. X            return(1);
  1032. X        }
  1033. X        memmove(pathend, lastend, prelen);
  1034. X        pathend += prelen;
  1035. X        *pathend = '\0';
  1036. X        lastend = stagel[stage];
  1037. X    }
  1038. X
  1039. X    if ((h = checkdir(pathbuf, pathend, 0)) == NULL) {
  1040. X        if (stage == 0 || direrr == H_NOREADDIR) {
  1041. X            printf("%s -> %s : directory %s does not %s.\n",
  1042. X                from, to, pathbuf, direrr == H_NOREADDIR ?
  1043. X                "allow reads/searches" : "exist");
  1044. X            paterr = 1;
  1045. X        }
  1046. X        return(stage);
  1047. X    }
  1048. X    di = h->h_di;
  1049. X
  1050. X    if (*lastend == ';') {
  1051. X        anylev = 1;
  1052. X        *start1 = pathend;
  1053. X        *len1 = 0;
  1054. X        lastend++;
  1055. X    }
  1056. X
  1057. X    nfils = di->di_nfils;
  1058. X
  1059. X#ifndef MSDOS
  1060. X    if ((op & MOVE) && !dwritable(h)) {
  1061. X        printf("%s -> %s : directory %s does not allow writes.\n",
  1062. X            from, to, pathbuf);
  1063. X        paterr = 1;
  1064. X        goto skiplev;
  1065. X    }
  1066. X#endif
  1067. X
  1068. X    firstesc = strchr(lastend, ESC);
  1069. X    if (firstesc == NULL || firstesc > firstwild[stage])
  1070. X        firstesc = firstwild[stage];
  1071. X    litlen = firstesc - lastend;
  1072. X    pf = di->di_fils + (i = ffirst(lastend, litlen, di));
  1073. X    if (i < nfils)
  1074. X    do {
  1075. X        if (
  1076. X            (try = trymatch(*pf, lastend)) != 0 &&
  1077. X            (
  1078. X                try == 1 ||
  1079. X                match(lastend + litlen, (*pf)->fi_name + litlen,
  1080. X                    start1 + anylev, len1 + anylev)
  1081. X            ) &&
  1082. X            keepmatch(*pf, pathend, &k, 0, wantdirs, laststage)
  1083. X        ) {
  1084. X            if (!laststage)
  1085. X                ret &= dostage(stager[stage], pathend + k,
  1086. X                    start1 + nwilds[stage], len1 + nwilds[stage],
  1087. X                    stage + 1, 0);
  1088. X            else {
  1089. X                ret = 0;
  1090. X                makerep();
  1091. X                if (badrep(h, *pf, &hto, &nto, &fdel, &flags))
  1092. X                    (*pf)->fi_rep = MISTAKE;
  1093. X                else {
  1094. X                    (*pf)->fi_rep = p = (REP *)challoc(sizeof(REP), 1);
  1095. X                    p->r_flags = flags | patflags;
  1096. X                    p->r_hfrom = h;
  1097. X                    p->r_ffrom = *pf;
  1098. X                    p->r_hto = hto;
  1099. X                    p->r_nto = nto;
  1100. X                    p->r_fdel = fdel;
  1101. X                    p->r_first = p;
  1102. X                    p->r_thendo = NULL;
  1103. X                    p->r_next = NULL;
  1104. X                    lastrep->r_next = p;
  1105. X                    lastrep = p;
  1106. X                    nreps++;
  1107. X                }
  1108. X            }
  1109. X        }
  1110. X        i++, pf++;
  1111. X    } while (i < nfils && strncmp(lastend, (*pf)->fi_name, litlen) == 0);
  1112. X
  1113. Xskiplev:
  1114. X    if (anylev)
  1115. X        for (pf = di->di_fils, i = 0; i < nfils; i++, pf++)
  1116. X            if (
  1117. X                *((*pf)->fi_name) != '.' &&
  1118. X#ifdef MSDOS
  1119. X                ((*pf)->fi_attrib & FA_DIREC) &&
  1120. X#endif
  1121. X                keepmatch(*pf, pathend, &k, 1, 1, 0)
  1122. X            ) {
  1123. X                *len1 = pathend - *start1 + k;
  1124. X                ret &= dostage(lastend, pathend + k, start1, len1, stage, 1);
  1125. X            }
  1126. X
  1127. X    return(ret);
  1128. X}
  1129. X
  1130. X
  1131. Xstatic int trymatch(ffrom, pat)
  1132. X    FILEINFO *ffrom;
  1133. X    char *pat;
  1134. X{
  1135. X    char *p;
  1136. X
  1137. X    if (ffrom->fi_rep != NULL)
  1138. X        return(0);
  1139. X
  1140. X    p = ffrom->fi_name;
  1141. X
  1142. X#ifdef MSDOS
  1143. X    if (*p == '.' || (!matchall && ffrom->fi_attrib & (FA_HIDDEN | FA_SYSTEM)))
  1144. X        return(strcmp(pat, p) == 0);
  1145. X#else
  1146. X    if (*p == '.')
  1147. X        if (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))
  1148. X            return(strcmp(pat, p) == 0);
  1149. X        else if (!matchall && *pat != '.')
  1150. X            return(0);
  1151. X#endif
  1152. X    return(-1);
  1153. X}
  1154. X
  1155. X
  1156. Xstatic int keepmatch(ffrom, pathend, pk, needslash, dirs, fils)
  1157. X    FILEINFO *ffrom;
  1158. X    char *pathend;
  1159. X    int *pk;
  1160. X    int needslash;
  1161. X    int dirs, fils;
  1162. X{
  1163. X    *pk = strlen(ffrom->fi_name);
  1164. X    if (pathend - pathbuf + *pk + needslash >= MAXPATH) {
  1165. X        *pathend = '\0';
  1166. X        printf("%s -> %s : search path %s%s too long.\n",
  1167. X            from, to, pathbuf, ffrom->fi_name);
  1168. X        paterr = 1;
  1169. X        return(0);
  1170. X    }
  1171. X    strcpy(pathend, ffrom->fi_name);
  1172. X#ifdef MSDOS
  1173. X    if ((ffrom->fi_attrib & FA_DIREC) ? !dirs : !fils)
  1174. X#else
  1175. X    getstat(pathbuf, ffrom);
  1176. X    if ((ffrom->fi_stflags & FI_ISDIR) ? !dirs : !fils)
  1177. X#endif
  1178. X        return(0);
  1179. X
  1180. X    if (needslash) {
  1181. X        strcpy(pathend + *pk, SLASHSTR);
  1182. X        (*pk)++;
  1183. X    }
  1184. X    return(1);
  1185. X}
  1186. X
  1187. X
  1188. Xstatic int badrep(hfrom, ffrom, phto, pnto, pfdel, pflags)
  1189. X    HANDLE *hfrom;
  1190. X    FILEINFO *ffrom;
  1191. X    HANDLE **phto;
  1192. X    char **pnto;
  1193. X    FILEINFO **pfdel;
  1194. X    int *pflags;
  1195. X{
  1196. X    char *f = ffrom->fi_name;
  1197. X
  1198. X    *pflags = 0;
  1199. X    if (
  1200. X#ifdef MSDOS
  1201. X        (ffrom->fi_attrib & FA_DIREC) &&
  1202. X#else
  1203. X        (ffrom->fi_stflags & FI_ISDIR) &&
  1204. X#endif
  1205. X        !(op & (DIRMOVE | SYMLINK))
  1206. X    )
  1207. X        printf("%s -> %s : source file is a directory.\n", pathbuf, fullrep);
  1208. X#ifndef MSDOS
  1209. X#ifndef SYSV
  1210. X    else if ((ffrom->fi_stflags & FI_LINKERR) && !(op & (MOVE | SYMLINK)))
  1211. X        printf("%s -> %s : source file is a badly aimed symbolic link.\n",
  1212. X            pathbuf, fullrep);
  1213. X    else if ((ffrom->fi_stflags & FI_NODEL) && (op & MOVE)) 
  1214. X        printf("%s -> %s : no delete permission for source file.\n",
  1215. X            pathbuf, fullrep);
  1216. X#endif
  1217. X    else if ((op & (COPY | APPEND)) && access(pathbuf, R_OK))
  1218. X        printf("%s -> %s : no read permission for source file.\n",
  1219. X            pathbuf, fullrep);
  1220. X#endif
  1221. X    else if (
  1222. X        *f == '.' &&
  1223. X        (f[1] == '\0' || strcmp(f, "..") == 0) &&
  1224. X        !(op & SYMLINK)
  1225. X    )
  1226. X        printf("%s -> %s : . and .. can't be renamed.\n", pathbuf, fullrep);
  1227. X    else if (repbad || checkto(hfrom, f, phto, pnto, pfdel) || badname(*pnto))
  1228. X        printf("%s -> %s : bad new name.\n", pathbuf, fullrep);
  1229. X    else if (*phto == NULL)
  1230. X        printf("%s -> %s : %s.\n", pathbuf, fullrep,
  1231. X#ifndef MSDOS
  1232. X            direrr == H_NOREADDIR ?
  1233. X            "no read or search permission for target directory" :
  1234. X#endif
  1235. X            "target directory does not exist");
  1236. X#ifndef MSDOS
  1237. X    else if (!dwritable(*phto))
  1238. X        printf("%s -> %s : no write permission for target directory.\n",
  1239. X            pathbuf, fullrep);
  1240. X#endif
  1241. X    else if (
  1242. X        (*phto)->h_di->di_vid != hfrom->h_di->di_vid &&
  1243. X        (*pflags = R_ISX, (op & (NORMMOVE | HARDLINK)))
  1244. X    )
  1245. X        printf("%s -> %s : cross-device move.\n",
  1246. X            pathbuf, fullrep);
  1247. X#ifndef MSDOS
  1248. X    else if (
  1249. X        *pflags && (op & MOVE) &&
  1250. X        !(ffrom->fi_stflags & FI_ISLNK) &&
  1251. X        access(pathbuf, R_OK)
  1252. X    )
  1253. X        printf("%s -> %s : no read permission for source file.\n",
  1254. X            pathbuf, fullrep);
  1255. X#ifndef SYSV
  1256. X    else if (
  1257. X        (op & SYMLINK) &&
  1258. X        !(
  1259. X            ((*phto)->h_di->di_vid == cwdv && (*phto)->h_di->di_did == cwdd) ||
  1260. X            *(hfrom->h_name) == SLASH ||
  1261. X            (*pflags |= R_ONEDIRLINK, hfrom->h_di == (*phto)->h_di)
  1262. X        )
  1263. X    )
  1264. X        printf("%s -> %s : symbolic link would be badly aimed.\n",
  1265. X            pathbuf, fullrep);
  1266. X#endif
  1267. X#endif
  1268. X    else
  1269. X        return(0);
  1270. X    badreps++;
  1271. X    return(-1);
  1272. X}
  1273. X
  1274. X
  1275. Xstatic int checkto(hfrom, f, phto, pnto, pfdel)
  1276. X    HANDLE *hfrom;
  1277. X    char *f;
  1278. X    HANDLE **phto;
  1279. X    char **pnto;
  1280. X    FILEINFO **pfdel;
  1281. X{
  1282. X    char tpath[MAXPATH + 1];
  1283. X    char *pathend;
  1284. X    FILEINFO *fdel;
  1285. X    int hlen, tlen;
  1286. X
  1287. X    if (op & DIRMOVE) {
  1288. X        *phto = hfrom;
  1289. X        hlen = strlen(hfrom->h_name);
  1290. X        pathend = fullrep + hlen;
  1291. X        memmove(pathend, fullrep, strlen(fullrep) + 1);
  1292. X        memmove(fullrep, hfrom->h_name, hlen);
  1293. X        if ((fdel = *pfdel = fsearch(pathend, hfrom->h_di)) != NULL) {
  1294. X            *pnto = fdel->fi_name;
  1295. X#ifndef MSDOS
  1296. X            getstat(fullrep, fdel);
  1297. X#endif
  1298. X        }
  1299. X        else
  1300. X            *pnto = mydup(pathend);
  1301. X    }
  1302. X    else {
  1303. X        pathend = getpath(tpath);
  1304. X        hlen = pathend - fullrep;
  1305. X        *phto = checkdir(tpath, tpath + hlen, 1);
  1306. X        if (
  1307. X            *phto != NULL &&
  1308. X            *pathend != '\0' &&
  1309. X            (fdel = *pfdel = fsearch(pathend, (*phto)->h_di)) != NULL &&
  1310. X#ifdef MSDOS
  1311. X            (fdel->fi_attrib & FA_DIREC)
  1312. X#else
  1313. X            (getstat(fullrep, fdel), fdel->fi_stflags & FI_ISDIR)
  1314. X#endif
  1315. X        ) {
  1316. X            tlen = strlen(pathend);
  1317. X            strcpy(pathend + tlen, SLASHSTR);
  1318. X            tlen++;
  1319. X            strcpy(tpath + hlen, pathend);
  1320. X            pathend += tlen;
  1321. X            hlen += tlen;
  1322. X            *phto = checkdir(tpath, tpath + hlen, 1);
  1323. X        }
  1324. X
  1325. X        if (*pathend == '\0') {
  1326. X            *pnto = f;
  1327. X            if (pathend - fullrep + strlen(f) >= MAXPATH) {
  1328. X                strcpy(fullrep, TOOLONG);
  1329. X                return(-1);
  1330. X            }
  1331. X            strcat(pathend, f);
  1332. X            if (*phto != NULL) {
  1333. X                fdel = *pfdel = fsearch(f, (*phto)->h_di);
  1334. X#ifndef MSDOS
  1335. X                if (fdel != NULL)
  1336. X                    getstat(fullrep, fdel);
  1337. X#endif
  1338. X            }
  1339. X        }
  1340. X        else if (fdel != NULL)
  1341. X            *pnto = fdel->fi_name;
  1342. X        else
  1343. X            *pnto = mydup(pathend);
  1344. X    }
  1345. X    return(0);
  1346. X}
  1347. X
  1348. X
  1349. Xstatic char *getpath(tpath)
  1350. X    char *tpath;
  1351. X{
  1352. X    char *pathstart, *pathend, c;
  1353. X
  1354. X#ifdef MSDOS
  1355. X    if (*fullrep != '\0' && fullrep[1] == ':')
  1356. X        pathstart = fullrep + 2;
  1357. X    else
  1358. X#endif
  1359. X        pathstart = fullrep;
  1360. X
  1361. X    pathend = pathstart + strlen(pathstart) - 1;
  1362. X    while (pathend >= pathstart && *pathend != SLASH)
  1363. X        --pathend;
  1364. X    pathend++;
  1365. X
  1366. X    c = *pathend;
  1367. X    *pathend = '\0';
  1368. X    strcpy(tpath, fullrep);
  1369. X    *pathend = c;
  1370. X    return(pathend);
  1371. X}
  1372. X
  1373. X
  1374. Xstatic int badname(s)
  1375. X    char *s;
  1376. X{
  1377. X    char *ext;
  1378. X
  1379. X    return (
  1380. X#ifdef MSDOS
  1381. X        *s == ' ' ||
  1382. X        *s == '.' ||
  1383. X        (ext = strchr(s, '.')) - s >= MAXFILE ||
  1384. X        (*ext == '.' && strchr(ext + 1, '.') != NULL) ||
  1385. X        strlen(ext) >= MAXEXT ||
  1386. X        strncmp(s, IDF, STRLEN(IDF)) == 0
  1387. X#else
  1388. X        (*s == '.' && (s[1] == '\0' || strcmp(s, "..") == 0)) ||
  1389. X        strlen(s) > MAXNAMLEN
  1390. X#endif
  1391. X    );
  1392. X}
  1393. X
  1394. X
  1395. X#ifndef MSDOS
  1396. Xstatic int getstat(ffull, f)
  1397. X    char *ffull;
  1398. X    FILEINFO *f;
  1399. X{
  1400. X    struct stat fstat;
  1401. X    int flags;
  1402. X
  1403. X    if ((flags = f->fi_stflags) & FI_STTAKEN)
  1404. X        return(flags & FI_LINKERR);
  1405. X    flags |= FI_STTAKEN;
  1406. X#ifdef SYSV
  1407. X    if (stat(ffull, &fstat)) {
  1408. X        fprintf("Strange, couldn't stat %s.\n", ffull);
  1409. X        quit();
  1410. X    }
  1411. X#else
  1412. X    if (lstat(ffull, &fstat)) {
  1413. X        fprintf("Strange, couldn't lstat %s.\n", ffull);
  1414. X        quit();
  1415. X    }
  1416. X    if ((flags & FI_INSTICKY) && fstat.st_uid != uid && uid != 0)
  1417. X        flags |= FI_NODEL;
  1418. X    if ((fstat.st_mode & S_IFMT) == S_IFLNK) {
  1419. X        flags |= FI_ISLNK;
  1420. X        if (stat(ffull, &fstat)) {
  1421. X            f->fi_stflags = flags | FI_LINKERR;
  1422. X            return(1);
  1423. X        }
  1424. X    }
  1425. X#endif
  1426. X    if ((fstat.st_mode & S_IFMT) == S_IFDIR)
  1427. X        flags |= FI_ISDIR;
  1428. X    f->fi_stflags = flags;
  1429. X    f->fi_mode = fstat.st_mode;
  1430. X    return(0);
  1431. X}
  1432. X
  1433. X
  1434. Xstatic int dwritable(h)
  1435. X    HANDLE *h;
  1436. X{
  1437. X    char *p = h->h_name, *myp, *lastslash = NULL, *pathend;
  1438. X    char *pw = &(h->h_di->di_flags), r;
  1439. X
  1440. X    if (uid == 0)
  1441. X        return(1);
  1442. X
  1443. X    if (*pw & DI_KNOWWRITE)
  1444. X        return(*pw & DI_CANWRITE);
  1445. X
  1446. X    pathend = p + strlen(p);
  1447. X    if (*p == '\0')
  1448. X        myp = ".";
  1449. X    else if (pathend == p + 1)
  1450. X        myp = SLASHSTR;
  1451. X    else {
  1452. X        lastslash = pathend - 1;
  1453. X        *lastslash = '\0';
  1454. X        myp = p;
  1455. X    }
  1456. X    r = !access(myp, W_OK) ? DI_CANWRITE : 0;
  1457. X    *pw |= DI_KNOWWRITE | r;
  1458. X
  1459. X    if (lastslash != NULL)
  1460. X        *lastslash = SLASH;
  1461. X    return(r);
  1462. X}
  1463. X
  1464. X
  1465. Xstatic int fwritable(hname, f)
  1466. X    char *hname;
  1467. X    FILEINFO *f;
  1468. X{
  1469. X    int r;
  1470. X
  1471. X    if (f->fi_stflags & FI_KNOWWRITE)
  1472. X        return(f->fi_stflags & FI_CANWRITE);
  1473. X
  1474. X    strcpy(fullrep, hname);
  1475. X    strcat(fullrep, f->fi_name);
  1476. X    r = !access(fullrep, W_OK) ? FI_CANWRITE : 0;
  1477. X    f->fi_stflags |= FI_KNOWWRITE | r;
  1478. X    return(r);
  1479. X}
  1480. X#endif
  1481. X
  1482. X
  1483. Xstatic FILEINFO *fsearch(s, d)
  1484. X    char *s;
  1485. X    DIRINFO *d;
  1486. X{
  1487. X    FILEINFO **fils = d->di_fils;
  1488. X    int nfils = d->di_nfils;
  1489. X    int first, k, last, res;
  1490. X
  1491. X    for(first = 0, last = nfils - 1;;) {
  1492. X        if (last < first)
  1493. X            return(NULL);
  1494. X        k = (first + last) >> 1;
  1495. X        if ((res = strcmp(s, fils[k]->fi_name)) == 0)
  1496. X            return(fils[k]);
  1497. X        if (res < 0)
  1498. X            last = k - 1;
  1499. X        else
  1500. X            first = k + 1;
  1501. X    }
  1502. X}
  1503. X
  1504. X
  1505. Xstatic int ffirst(s, n, d)
  1506. X    char *s;
  1507. X    int n;
  1508. X    DIRINFO *d;
  1509. X{
  1510. X    int first, k, last, res;
  1511. X    FILEINFO **fils = d->di_fils;
  1512. X    int nfils = d->di_nfils;
  1513. X
  1514. X    if (nfils == 0 || n == 0)
  1515. X        return(0);
  1516. X    first = 0;
  1517. X    last = nfils - 1;
  1518. X    for(;;) {
  1519. X        k = (first + last) >> 1;
  1520. X        res = strncmp(s, fils[k]->fi_name, n);
  1521. X        if (first == last)
  1522. X            return(res == 0 ? k : nfils);
  1523. X        else if (res > 0)
  1524. X            first = k + 1;
  1525. X        else
  1526. X            last = k;
  1527. X    }
  1528. X}
  1529. X
  1530. X
  1531. X#ifdef MSDOS
  1532. X/* checkdir and takedir for MS-D*S */
  1533. X
  1534. Xstatic HANDLE *checkdir(p, pathend, which)
  1535. X    char *p, *pathend;
  1536. X    int which;
  1537. X{
  1538. X    struct ffblk de;
  1539. X    DIRID d;
  1540. X    DEVID v;
  1541. X    HANDLE *h;
  1542. X    char *dirstart = p;
  1543. X    int fd;
  1544. X    int firstfound;
  1545. X    DIRINFO *di;
  1546. X
  1547. X    if (hsearch(p, which, &h))
  1548. X        if (h->h_di == NULL) {
  1549. X            direrr = h->h_err;
  1550. X            return(NULL);
  1551. X        }
  1552. X        else
  1553. X            return(h);
  1554. X
  1555. X    if (*p == '\0' || p[1] != ':')
  1556. X        v = curdisk;
  1557. X    else {
  1558. X        dirstart += 2;
  1559. X        v = mylower(p[0]) - 'a';
  1560. X        if (v < 0 || v >= maxdisk)
  1561. X            return(NULL);
  1562. X    }
  1563. X
  1564. X    if (patch.ph_safeid) {
  1565. X        strcpy(pathend, IDF);
  1566. X        strcpy(pathend + STRLEN(IDF), "*");
  1567. X        if (findfirst(p, &de, 0)) {
  1568. X            if ((d = ndirs) == 1000) {
  1569. X                fprintf(stderr, "Too many different directories.\n");
  1570. X                quit();
  1571. X            }
  1572. X            sprintf(pathend + STRLEN(IDF), "%03d", d);
  1573. X            if ((fd = _creat(p, 0)) < 0) {
  1574. X                direrr = h->h_err = H_NODIR;
  1575. X                return(NULL);
  1576. X            }
  1577. X            _close(fd);
  1578. X            strcpy(pathend, "*.*");
  1579. X            if (findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN))
  1580. X                h->h_di = dadd(v, d);
  1581. X            else
  1582. X                takedir(&de, h->h_di = dadd(v, d));
  1583. X        }
  1584. X        else if ((d = atoi(de.ff_name + STRLEN(IDF))) < ndirs)
  1585. X            h->h_di = dirs[d];
  1586. X        else {
  1587. X            strcpy(pathend, de.ff_name);
  1588. X            fprintf(stderr, "Strange dir-id file encountered: %s.\n", p);
  1589. X            quit();
  1590. X        }
  1591. X        *pathend = '\0';
  1592. X    }
  1593. X    else {
  1594. X        strcpy(pathend, "*.*");
  1595. X        firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
  1596. X        *pathend = '\0';
  1597. X        if (firstfound) {
  1598. X            v = DRIVENO(&de);
  1599. X            d = CLUSTNO(&de);
  1600. X        }
  1601. X        else {
  1602. X            strcpy(pathend, "T.D");
  1603. X            if (mkdir(p)) {
  1604. X                *pathend = '\0';
  1605. X                direrr = h->h_err = H_NODIR;
  1606. X                return(NULL);
  1607. X            }
  1608. X            strcpy(pathend, "*.*");
  1609. X            firstfound = !findfirst(p, &de, FA_DIREC | FA_SYSTEM | FA_HIDDEN);
  1610. X            *pathend = '\0';
  1611. X            v = DRIVENO(&de);
  1612. X            d = CLUSTNO(&de);
  1613. X            rmdir(p);
  1614. X            if (!firstfound || d != 0) {
  1615. X                fprintf(stderr,
  1616. X                    "Strange, %s does not seem to be a root dir.\n",
  1617. X                    p);
  1618. X                quit();
  1619. X            }
  1620. X        }
  1621. X
  1622. X        if ((di = dsearch(v, d)) == NULL)
  1623. X            if (firstfound)
  1624. X                takedir(&de, h->h_di = dadd(v, d));
  1625. X            else
  1626. X                h->h_di = dadd(v, d);
  1627. X        else
  1628. X            h->h_di = di;
  1629. X    }
  1630. X
  1631. X    return(h);
  1632. X}
  1633. X
  1634. X
  1635. Xstatic void takedir(pff, di)
  1636. X    struct ffblk *pff;
  1637. X    DIRINFO *di;
  1638. X{
  1639. X    int cnt, room, namlen, needdot;
  1640. X    FILEINFO **fils, *f;
  1641. X    char c, *p, *p1;
  1642. X
  1643. X    room = INITROOM;
  1644. X    di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
  1645. X    cnt = 0;
  1646. X    do {
  1647. X        if (strnicmp(pff->ff_name, IDF, STRLEN(IDF)) == 0)
  1648. X            continue;
  1649. X        if (cnt == room) {
  1650. X            room *= 2;
  1651. X            fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
  1652. X            memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
  1653. X            chgive(di->di_fils, cnt * sizeof(FILEINFO *));
  1654. X            di->di_fils = fils;
  1655. X            fils = di->di_fils + cnt;
  1656. X        }
  1657. X        needdot = 1;
  1658. X        for (p = pff->ff_name, namlen = 0; (c = *p) != '\0'; p++, namlen++)
  1659. X            if (c == '.')
  1660. X                needdot = 0;
  1661. X        *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
  1662. X        f->fi_name = p = (char *)challoc(namlen + needdot + 1, 0);
  1663. X        for (p1 = pff->ff_name; (c = *p1) != '\0'; p1++)
  1664. X            *(p++) = mylower(c);
  1665. X        if (needdot)
  1666. X            *(p++) = '.';
  1667. X        *p = '\0';
  1668. X        f->fi_attrib = pff->ff_attrib;
  1669. X        f->fi_rep = NULL;
  1670. X        cnt++;
  1671. X        fils++;
  1672. X    } while (findnext(pff) == 0);
  1673. X    qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
  1674. X    di->di_nfils = cnt;
  1675. X}
  1676. X
  1677. X#else
  1678. X/* checkdir, takedir for Un*x */
  1679. X
  1680. Xstatic HANDLE *checkdir(p, pathend, which)
  1681. X    char *p, *pathend;
  1682. X    int which;
  1683. X{
  1684. X    struct stat dstat;
  1685. X    DIRID d;
  1686. X    DEVID v;
  1687. X    DIRINFO **newdirs, *di;
  1688. X    int nfils;
  1689. X    FILEINFO **fils;
  1690. X    char *myp, *lastslash = NULL;
  1691. X    int sticky;
  1692. X    HANDLE *h;
  1693. X
  1694. X    if (hsearch(p, which, &h))
  1695. X        if (h->h_di == NULL) {
  1696. X            direrr = h->h_err;
  1697. X            return(NULL);
  1698. X        }
  1699. X        else
  1700. X            return(h);
  1701. X
  1702. X    if (*p == '\0')
  1703. X        myp = ".";
  1704. X    else if (pathend == p + 1)
  1705. X        myp = SLASHSTR;
  1706. X    else {
  1707. X        lastslash = pathend - 1;
  1708. X        *lastslash = '\0';
  1709. X        myp = p;
  1710. X    }
  1711. X
  1712. X    if (stat(myp, &dstat) || (dstat.st_mode & S_IFMT) != S_IFDIR)
  1713. X        direrr = h->h_err = H_NODIR;
  1714. X    else if (access(myp, R_OK | X_OK))
  1715. X        direrr = h->h_err = H_NOREADDIR;
  1716. X    else {
  1717. X        direrr = 0;
  1718. X        sticky = (dstat.st_mode & S_ISVTX) && uid != 0 && uid != dstat.st_uid ?
  1719. X            FI_INSTICKY : 0;
  1720. X        v = dstat.st_dev;
  1721. X        d = dstat.st_ino;
  1722. X
  1723. X        if ((di = dsearch(v, d)) == NULL)
  1724. X            takedir(myp, di = dadd(v, d), sticky);
  1725. X    }
  1726. X
  1727. X    if (lastslash != NULL)
  1728. X        *lastslash = SLASH;
  1729. X    if (direrr != 0)
  1730. X        return(NULL);
  1731. X    h->h_di = di;
  1732. X    return(h);
  1733. X}
  1734. X
  1735. X
  1736. Xstatic void takedir(p, di, sticky)
  1737. X    char *p;
  1738. X    DIRINFO *di;
  1739. X    int sticky;
  1740. X{
  1741. X    int cnt, room;
  1742. X    DIRENTRY *dp;
  1743. X    FILEINFO *f, **fils;
  1744. X    DIR *dirp;
  1745. X
  1746. X    if ((dirp = opendir(p)) == NULL) {
  1747. X        fprintf(stderr, "Strange, can't scan %s.\n", p);
  1748. X        quit();
  1749. X    }
  1750. X    room = INITROOM;
  1751. X    di->di_fils = fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
  1752. X    cnt = 0;
  1753. X    while ((dp = readdir(dirp)) != NULL) {
  1754. X        if (cnt == room) {
  1755. X            room *= 2;
  1756. X            fils = (FILEINFO **)myalloc(room * sizeof(FILEINFO *));
  1757. X            memcpy(fils, di->di_fils, cnt * sizeof(FILEINFO *));
  1758. X            chgive(di->di_fils, cnt * sizeof(FILEINFO *));
  1759. X            di->di_fils = fils;
  1760. X            fils = di->di_fils + cnt;
  1761. X        }
  1762. X        *fils = f = (FILEINFO *)challoc(sizeof(FILEINFO), 1);
  1763. X        f->fi_name = mydup(dp->d_name);
  1764. X        f->fi_stflags = sticky;
  1765. X        f->fi_rep = NULL;
  1766. X        cnt++;
  1767. X        fils++;
  1768. X    }
  1769. X    closedir(dirp);
  1770. X    qsort(di->di_fils, cnt, sizeof(FILEINFO *), fcmp);
  1771. X    di->di_nfils = cnt;
  1772. X}
  1773. X
  1774. X/* end of Un*x checkdir, takedir; back to general program */
  1775. X#endif
  1776. X
  1777. X
  1778. Xstatic int fcmp(pf1, pf2)
  1779. X    FILEINFO **pf1, **pf2;
  1780. X{
  1781. X        return(strcmp((*pf1)->fi_name, (*pf2)->fi_name));
  1782. X}
  1783. X
  1784. X
  1785. Xstatic HANDLE *hadd(n)
  1786. X    char *n;
  1787. X{
  1788. X    HANDLE **newhandles, *h;
  1789. X
  1790. X    if (nhandles == handleroom) {
  1791. X        handleroom *= 2;
  1792. X        newhandles = (HANDLE **)myalloc(handleroom * sizeof(HANDLE *));
  1793. X        memcpy(newhandles, handles, nhandles * sizeof(HANDLE *));
  1794. X        chgive(handles, nhandles * sizeof(HANDLE *));
  1795. X        handles = newhandles;
  1796. X    }
  1797. X    handles[nhandles++] = h = (HANDLE *)challoc(sizeof(HANDLE), 1);
  1798. X    h->h_name = (char *)challoc(strlen(n) + 1, 0);
  1799. X    strcpy(h->h_name, n);
  1800. X    h->h_di = NULL;
  1801. X    return(h);
  1802. X}
  1803. X
  1804. X
  1805. Xstatic int hsearch(n, which, pret)
  1806. X    char *n;
  1807. X    int which;
  1808. X    HANDLE **pret;
  1809. X{
  1810. X    int i;
  1811. X    HANDLE **ph;
  1812. X
  1813. X    if (strcmp(n, lasthandle[which]->h_name) == 0) {
  1814. X        *pret = lasthandle[which];
  1815. X        return(1);
  1816. X    }
  1817. X
  1818. X    for(i = 0, ph = handles; i < nhandles; i++, ph++)
  1819. X        if (strcmp(n, (*ph)->h_name) == 0) {
  1820. X            lasthandle[which] = *pret = *ph;
  1821. X            return(1);
  1822. X        }
  1823. X
  1824. X    lasthandle[which] = *pret = hadd(n);
  1825. X    return(0);
  1826. X}
  1827. X
  1828. X
  1829. Xstatic DIRINFO *dadd(v, d)
  1830. X    DEVID v;
  1831. X    DIRID d;
  1832. X{
  1833. X    DIRINFO *di;
  1834. X    DIRINFO **newdirs;
  1835. X
  1836. X    if (ndirs == dirroom) {
  1837. X        dirroom *= 2;
  1838. X        newdirs = (DIRINFO **)myalloc(dirroom * sizeof(DIRINFO *));
  1839. X        memcpy(newdirs, dirs, ndirs * sizeof(DIRINFO *));
  1840. X        chgive(dirs, ndirs * sizeof(DIRINFO *));
  1841. X        dirs = newdirs;
  1842. X    }
  1843. X    dirs[ndirs++] = di = (DIRINFO *)challoc(sizeof(DIRINFO), 1);
  1844. X    di->di_vid = v;
  1845. X    di->di_did = d;
  1846. X    di->di_nfils = 0;
  1847. X    di->di_fils = NULL;
  1848. X    di->di_flags = 0;
  1849. X    return(di);
  1850. X}
  1851. X
  1852. X
  1853. Xstatic DIRINFO *dsearch(v, d)
  1854. X    DEVID v;
  1855. X    DIRID d;
  1856. X{
  1857. X    int i;
  1858. X    DIRINFO *di;
  1859. X
  1860. X    for(i = 0, di = *dirs; i < ndirs; i++, di++)
  1861. X        if (v == di->di_vid && d == di->di_did)
  1862. X            return(di);
  1863. X    return(NULL);
  1864. X}
  1865. X
  1866. X
  1867. Xstatic int match(pat, s, start1, len1)
  1868. X    char *pat, *s, **start1;
  1869. X    int *len1;
  1870. X{
  1871. X    char c, *olds;
  1872. X
  1873. X    *start1 = 0;
  1874. X    for(;;)
  1875. X        switch (c = *pat) {
  1876. X        case '\0':
  1877. X        case SLASH:
  1878. X            return(*s == '\0');
  1879. X#ifdef MSDOS
  1880. X        case '!':
  1881. X            *start1 = olds = s;
  1882. X            if ((s = strchr(s, '.')) == NULL)
  1883. X                return(0);
  1884. X            s++;
  1885. X            *len1 = s - olds;
  1886. X            if ((c = *(++pat)) == '\0') {
  1887. X                *len1 += strlen(s);
  1888. X                return(1);
  1889. X            }
  1890. X            for ( ; !match(pat, s, start1 + 1, len1 + 1); (*len1)++, s++)
  1891. X                if (*s == '\0')
  1892. X                    return(0);
  1893. X            return(1);
  1894. X#endif
  1895. X        case '*':
  1896. X            *start1 = s;
  1897. X            if ((c = *(++pat)) == '\0') {
  1898. X                *len1 = strlen(s);
  1899. X                return(1);
  1900. X            }
  1901. X            else {
  1902. X                for (*len1=0; !match(pat, s, start1+1, len1+1); (*len1)++, s++)
  1903. X                    if (
  1904. X#ifdef MSDOS
  1905. X                        *s == '.' ||
  1906. X#endif
  1907. X                        *s == '\0'
  1908. X                    )
  1909. X                        return(0);
  1910. X                return(1);
  1911. X            }
  1912. X        case '?':
  1913. X            if (
  1914. X#ifdef MSDOS
  1915. X                *s == '.' ||
  1916. X#endif
  1917. X                *s == '\0'
  1918. X            )
  1919. X                return(0);
  1920. X            *(start1++) = s;
  1921. X            *(len1++) = 1;
  1922. X            pat++;
  1923. X            s++;
  1924. X            break;
  1925. X        case '[':
  1926. X            {
  1927. X                int matched = 0, notin = 0, inrange = 0;
  1928. X                char prevc = '\0';
  1929. X
  1930. X                if ((c = *(++pat)) == '^') {
  1931. X                    notin = 1;
  1932. X                    c = *(++pat);
  1933. X                }
  1934. X                while (c != ']') {
  1935. X                    if (c == '-' && !inrange)
  1936. X                        inrange = 1;
  1937. X                    else {
  1938. X                        if (c == ESC) {
  1939. X                            c = *(++pat);
  1940. X                        }
  1941. X                        if (inrange) {
  1942. X                            if (*s >= prevc && *s <= c)
  1943. X                                matched = 1;
  1944. X                            inrange = 0;
  1945. X                        }
  1946. X                        else if (c == *s)
  1947. X                            matched = 1;
  1948. X                        prevc = c;
  1949. X                    }
  1950. X                    c = *(++pat);
  1951. X                }
  1952. X                if (inrange && *s >= prevc)
  1953. X                    matched = 1;
  1954. X                if (!(matched ^ notin))
  1955. X                    return(0);
  1956. X                *(start1++) = s;
  1957. X                *(len1++) = 1;
  1958. X                pat++;
  1959. X                s++;
  1960. X            }
  1961. X            break;
  1962. X        case ESC:
  1963. X            c = *(++pat);
  1964. X        default:
  1965. X            if (c == *s) {
  1966. X                 pat++;
  1967. X                s++;
  1968. X            }
  1969. X            else
  1970. X                return(0);
  1971. X        }
  1972. X}
  1973. X
  1974. X
  1975. Xstatic void makerep()
  1976. X{
  1977. X    int l, x;
  1978. X#ifndef MSDOS
  1979. X    int i, cnv;
  1980. X    char *q;
  1981. X#endif
  1982. X    char *p, *pat, c, pc;
  1983. X
  1984. X    repbad = 0;
  1985. X    p = fullrep;
  1986. X    for (pat = to, l = 0; (c = *pat) != '\0'; pat++, l++) {
  1987. X        if (c == '=') {
  1988. X            c = *(++pat);
  1989. X#ifndef MSDOS
  1990. X            if (c == 'l') {
  1991. X                cnv = LOWER;
  1992. X                c = *(++pat);
  1993. X            }
  1994. X            if (c == 'u') {
  1995. X                cnv = UPPER;
  1996. X                c = *(++pat);
  1997. X            }
  1998. X            else
  1999. X                cnv = STAY;
  2000. X#endif
  2001. X            for(x = 0; ;x *= 10) {
  2002. X                x += c - '0';
  2003. X                c = *(pat+1);
  2004. X                if (!isdigit(c))
  2005. X                    break;
  2006. X                pat++;
  2007. X            }
  2008. X            --x;
  2009. X            if (l + len[x] >= MAXPATH)
  2010. X                goto toolong;
  2011. X#ifdef MSDOS
  2012. X            if (
  2013. X                *(start[x]) == '.' &&
  2014. X                (
  2015. X                    p == fullrep ||
  2016. X                    *(p - 1) == SLASH
  2017. X                )
  2018. X            ) {
  2019. X                repbad = 1;
  2020. X                if (l + STRLEN(EMPTY) >= MAXPATH)
  2021. X                    goto toolong;
  2022. X                strcpy(p, EMPTY);
  2023. X                p += STRLEN(EMPTY);
  2024. X                l += STRLEN(EMPTY);
  2025. X            }
  2026. X#else
  2027. X            switch (cnv) {
  2028. X            case STAY:
  2029. X#endif
  2030. X                memmove(p, start[x], len[x]);
  2031. X                p += len[x];
  2032. X#ifndef MSDOS
  2033. X                break;
  2034. X            case LOWER:
  2035. X                for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
  2036. X                    *p = mylower(*q);
  2037. X                break;
  2038. X            case UPPER:
  2039. X                for (i = len[x], q = start[x]; i > 0; i--, p++, q++)
  2040. X                    *p = myupper(*q);
  2041. X            }
  2042. X#endif
  2043. X        }
  2044. X        else {
  2045. X            if (c == ESC)
  2046. X                c = *(++pat);
  2047. X            if (l == MAXPATH)
  2048. X                goto toolong;
  2049. X            if (
  2050. X                (
  2051. X#ifdef MSDOS
  2052. X                    c == '.' ||
  2053. X#endif
  2054. X                    c == SLASH
  2055. X                ) &&
  2056. X                (
  2057. X                    p == fullrep ? pat != to :
  2058. X                    (
  2059. X                        (
  2060. X                            (pc = *(p - 1)) == SLASH
  2061. X#ifdef MSDOS
  2062. X                            || pc == ':'
  2063. X#endif
  2064. X                        ) &&
  2065. X                         *(pat - 1) != pc
  2066. X                    )
  2067. X                )
  2068. X            ) {
  2069. X                repbad = 1;
  2070. X                if (l + STRLEN(EMPTY) >= MAXPATH)
  2071. X                    goto toolong;
  2072. X                strcpy(p, EMPTY);
  2073. X                p += STRLEN(EMPTY);
  2074. X                l += STRLEN(EMPTY);
  2075. X            }
  2076. X            *(p++)= c;
  2077. X        }
  2078. X    }
  2079. X    if (p == fullrep) {
  2080. X        strcpy(fullrep, EMPTY);
  2081. X        repbad = 1;
  2082. X    }
  2083. X    *(p++) = '\0';
  2084. X    return;
  2085. X
  2086. Xtoolong:
  2087. X    repbad = 1;
  2088. X    strcpy(fullrep, TOOLONG);
  2089. X}
  2090. X
  2091. X
  2092. Xstatic void checkcollisions()
  2093. X{
  2094. X    REPDICT *rd, *prd;
  2095. X    REP *p, *q;
  2096. X    int i, mult, oldnreps;
  2097. X
  2098. X    if (nreps == 0)
  2099. X        return;
  2100. X    rd = (REPDICT *)myalloc(nreps * sizeof(REPDICT));
  2101. X    for (
  2102. X        q = &hrep, p = q->r_next, prd = rd, i = 0;
  2103. X        p != NULL;
  2104. X        q = p, p = p->r_next, prd++, i++
  2105. X    ) {
  2106. X        prd->rd_p = p;
  2107. X        prd->rd_dto = p->r_hto->h_di;
  2108. X        prd->rd_nto = p->r_nto;
  2109. X        prd->rd_i = i;
  2110. X    }
  2111. X    qsort(rd, nreps, sizeof(REPDICT), rdcmp);
  2112. X    mult = 0;
  2113. X    for (i = 0, prd = rd, oldnreps = nreps; i < oldnreps; i++, prd++)
  2114. X        if (
  2115. X            i < oldnreps - 1 &&
  2116. X            prd->rd_dto == (prd + 1)->rd_dto &&
  2117. X            strcmp(prd->rd_nto, (prd + 1)->rd_nto) == 0
  2118. X        ) {
  2119. X            if (!mult)
  2120. X                mult = 1;
  2121. X            else
  2122. X                printf(" , ");
  2123. X            printf("%s%s", prd->rd_p->r_hfrom->h_name,
  2124. X                prd->rd_p->r_ffrom->fi_name);
  2125. X            prd->rd_p->r_flags |= R_SKIP;
  2126. X            prd->rd_p->r_ffrom->fi_rep = MISTAKE;
  2127. X            nreps--;
  2128. X            badreps++;
  2129. X        }
  2130. X        else if (mult) {
  2131. X            prd->rd_p->r_flags |= R_SKIP;
  2132. X            prd->rd_p->r_ffrom->fi_rep = MISTAKE;
  2133. X            nreps--;
  2134. X            badreps++;
  2135. X            printf(" , %s%s -> %s%s : collision.\n",
  2136. X                prd->rd_p->r_hfrom->h_name, prd->rd_p->r_ffrom->fi_name,
  2137. X                prd->rd_p->r_hto->h_name, prd->rd_nto);
  2138. X            mult = 0;
  2139. X        }
  2140. X    chgive(rd, oldnreps * sizeof(REPDICT));
  2141. X}
  2142. X
  2143. X
  2144. Xstatic int rdcmp(rd1, rd2)
  2145. X    REPDICT *rd1, *rd2;
  2146. X{
  2147. X    int ret;
  2148. X
  2149. X    if (
  2150. X        (ret = rd1->rd_dto - rd2->rd_dto) == 0 &&
  2151. X        (ret = strcmp(rd1->rd_nto, rd2->rd_nto)) == 0
  2152. X    )
  2153. X        ret = rd1->rd_i - rd2->rd_i;
  2154. X    return(ret);
  2155. X}
  2156. X
  2157. X
  2158. Xstatic void findorder()
  2159. X{
  2160. X    REP *p, *q, *t, *first, *pred;
  2161. X    FILEINFO *fi;
  2162. X
  2163. X    for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
  2164. X        if (p->r_flags & R_SKIP) {
  2165. X            q->r_next = p->r_next;
  2166. X            p = q;
  2167. X        }
  2168. X        else if (
  2169. X            (fi = p->r_fdel) == NULL ||
  2170. X            (pred = fi->fi_rep) == NULL ||
  2171. X            pred == MISTAKE
  2172. X        )
  2173. X            continue;
  2174. X        else if ((first = pred->r_first) == p) {
  2175. X            p->r_flags |= R_ISCYCLE;
  2176. X            pred->r_flags |= R_ISALIASED;
  2177. X            if (op & MOVE)
  2178. X                p->r_fdel = NULL;
  2179. X        }
  2180. X        else {
  2181. X            if (op & MOVE)
  2182. X                p->r_fdel = NULL;
  2183. X            while (pred->r_thendo != NULL)
  2184. X                pred = pred->r_thendo;
  2185. X            pred->r_thendo = p;
  2186. X            for (t = p; t != NULL; t = t->r_thendo)
  2187. X                t->r_first = first;
  2188. X            q->r_next = p->r_next;
  2189. X            p = q;
  2190. X        }
  2191. X}
  2192. X
  2193. X
  2194. Xstatic void nochains()
  2195. X{
  2196. X    REP *p, *q;
  2197. X
  2198. X    for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next)
  2199. X        if (p->r_flags & R_ISCYCLE || p->r_thendo != NULL) {
  2200. X            printchain(p);
  2201. X            printf("%s%s : no chain copies allowed.\n",
  2202. X                p->r_hto->h_name, p->r_nto);
  2203. X            q->r_next = p->r_next;
  2204. X            p = q;
  2205. X        }
  2206. X}
  2207. X
  2208. X
  2209. Xstatic void printchain(p)
  2210. X    REP *p;
  2211. X{
  2212. X    if (p->r_thendo != NULL)
  2213. X        printchain(p->r_thendo);
  2214. X    printf("%s%s -> ", p->r_hfrom->h_name, p->r_ffrom->fi_name);
  2215. X    badreps++;
  2216. X    nreps--;
  2217. X    p->r_ffrom->fi_rep = MISTAKE;
  2218. X}
  2219. X
  2220. X
  2221. Xstatic void scandeletes(pkilldel)
  2222. X    int (*pkilldel)();
  2223. X{
  2224. X    REP *p, *q, *n;
  2225. X
  2226. X    for (q = &hrep, p = q->r_next; p != NULL; q = p, p = p->r_next) {
  2227. X        if (p->r_fdel != NULL)
  2228. X            while ((*pkilldel)(p)) {
  2229. X                nreps--;
  2230. X                p->r_ffrom->fi_rep = MISTAKE;
  2231. X                if ((n = p->r_thendo) != NULL) {
  2232. X                    if (op & MOVE)
  2233. X                        n->r_fdel = p->r_ffrom;
  2234. X                    n->r_next = p->r_next;
  2235. X                    q->r_next = p = n;
  2236. X                }
  2237. X                else {
  2238. X                    q->r_next = p->r_next;
  2239. X                    p = q;
  2240. X                    break;
  2241. X                }
  2242. X            }
  2243. X    }
  2244. X}
  2245. X
  2246. X
  2247. Xstatic int baddel(p)
  2248. X    REP *p;
  2249. X{
  2250. X    HANDLE *hfrom = p->r_hfrom, *hto = p->r_hto;
  2251. X    FILEINFO *fto = p->r_fdel;
  2252. X    char *t = fto->fi_name, *f = p->r_ffrom->fi_name;
  2253. X    char *hnf = hfrom->h_name, *hnt = hto->h_name;
  2254. X
  2255. X    if (delstyle == NODEL && !(p->r_flags & R_DELOK) && !(op & APPEND))
  2256. X        printf("%s%s -> %s%s : old %s%s would have to be %s.\n",
  2257. X            hnf, f, hnt, t, hnt, t,
  2258. X            (op & OVERWRITE) ? "overwritten" : "deleted");
  2259. X    else if (fto->fi_rep == MISTAKE)
  2260. X        printf("%s%s -> %s%s : old %s%s was to be done first.\n",
  2261. X            hnf, f, hnt, t, hnt, t);
  2262. X    else if (
  2263. X#ifdef MSDOS
  2264. X        fto->fi_attrib & FA_DIREC
  2265. X#else
  2266. X        fto->fi_stflags & FI_ISDIR
  2267. X#endif
  2268. X    )
  2269. X        printf("%s%s -> %s%s : %s%s%s is a directory.\n",
  2270. X            hnf, f, hnt, t, (op & APPEND) ? "" : "old ", hnt, t);
  2271. X#ifndef MSDOS
  2272. X    else if ((fto->fi_stflags & FI_NODEL) && !(op & (APPEND | OVERWRITE)))
  2273. X        printf("%s%s -> %s%s : old %s%s lacks delete permission.\n",
  2274. X            hnf, f, hnt, t, hnt, t);
  2275. X#endif
  2276. X    else if (
  2277. X        (op & (APPEND | OVERWRITE)) &&
  2278. X#ifdef MSDOS
  2279. X        fto->fi_attrib & FA_RDONLY
  2280. X#else
  2281. X        !fwritable(hnt, fto)
  2282. X#endif
  2283. X    ) {
  2284. X        printf("%s%s -> %s%s : %s%s %s.\n",
  2285. X            hnf, f, hnt, t, hnt, t,
  2286. X#ifndef MSDOS
  2287. X#ifndef SYSV
  2288. X            fto->fi_stflags & FI_LINKERR ?
  2289. X            "is a badly aimed symbolic link" :
  2290. X#endif
  2291. X#endif
  2292. X            "lacks write permission");
  2293. X    }
  2294. X    else
  2295. X        return(0);
  2296. X    badreps++;
  2297. X    return(1);
  2298. X}
  2299. X
  2300. X
  2301. Xstatic int skipdel(p)
  2302. X    REP *p;
  2303. X{
  2304. X    if (p->r_flags & R_DELOK)
  2305. X        return(0);
  2306. X    fprintf(stderr, "%s%s -> %s%s : ",
  2307. X        p->r_hfrom->h_name, p->r_ffrom->fi_name,
  2308. X        p->r_hto->h_name, p->r_nto);
  2309. X    if (
  2310. X#ifdef MSDOS
  2311. X        p->r_fdel->fi_attrib & FA_RDONLY
  2312. X#else
  2313. X#ifndef SYSV
  2314. X        !(p->r_ffrom->fi_stflags & FI_ISLNK) &&
  2315. X#endif
  2316. X        !fwritable(p->r_hto->h_name, p->r_fdel)
  2317. X#endif
  2318. X    )
  2319. X        fprintf(stderr, "old %s%s lacks write permission. delete it",
  2320. X            p->r_hto->h_name, p->r_nto);
  2321. X    else
  2322. X        fprintf(stderr, "%s old %s%s",
  2323. X            (op & OVERWRITE) ? "overwrite" : "delete",
  2324. X            p->r_hto->h_name, p->r_nto);
  2325. X    return(!getreply("? ", -1));
  2326. X}
  2327. X
  2328. X
  2329. Xstatic void goonordie()
  2330. X{
  2331. X    if ((paterr || badreps) && nreps > 0) {
  2332. X        fprintf(stderr, "Not everything specified can be done.");
  2333. X        if (badstyle == ABORTBAD) {
  2334. X            fprintf(stderr, " Aborting.\n");
  2335. X            exit(1);
  2336. X        }
  2337. X        else if (badstyle == SKIPBAD)
  2338. X            fprintf(stderr, " Proceeding with the rest.\n");
  2339. X        else if (!getreply(" Proceed with the rest? ", -1))
  2340. X            exit(1);
  2341. X    }
  2342. X}
  2343. X
  2344. X
  2345. Xstatic void doreps()
  2346. X{
  2347. X    char *fstart;
  2348. X    int k, printaliased = 0, alias;
  2349. X    REP *first, *p;
  2350. X    long aliaslen;
  2351. X
  2352. X#ifdef MSDOS
  2353. X    ctrlbrk(breakrep);
  2354. X#else
  2355. X    signal(SIGINT, breakrep);
  2356. X#endif
  2357. X
  2358. X    for (first = hrep.r_next, k = 0; first != NULL; first = first->r_next) {
  2359. X        for (p = first; p != NULL; p = p->r_thendo, k++) {
  2360. X            if (gotsig) {
  2361. X                fflush(stdout);
  2362. X                fprintf(stderr, "User break.\n");
  2363. X                printaliased = snap(first, p);
  2364. X                gotsig = 0;
  2365. X            }
  2366. X            strcpy(fullrep, p->r_hto->h_name);
  2367. X            strcat(fullrep, p->r_nto);
  2368. X            if (!noex && (p->r_flags & R_ISCYCLE))
  2369. X                if (op & APPEND)
  2370. X                    aliaslen = appendalias(first, p, &printaliased);
  2371. X                else
  2372. X                    alias = movealias(first, p, &printaliased);
  2373. X            strcpy(pathbuf, p->r_hfrom->h_name);
  2374. X            fstart = pathbuf + strlen(pathbuf);
  2375. X            if ((p->r_flags & R_ISALIASED) && !(op & APPEND))
  2376. X                sprintf(fstart, "%s%03d", TEMP, alias);
  2377. X            else
  2378. X                strcpy(fstart, p->r_ffrom->fi_name);
  2379. X            if (!noex) {
  2380. X                if (p->r_fdel != NULL && !(op & (APPEND | OVERWRITE)))
  2381. X                    myunlink(fullrep, p->r_fdel);
  2382. X                if (
  2383. X                    (op & (COPY | APPEND)) ?
  2384. X                        copy(p->r_ffrom,
  2385. X                            p->r_flags & R_ISALIASED ? aliaslen : -1) :
  2386. X#ifndef MSDOS
  2387. X                    (op & HARDLINK) ?
  2388. X                        link(pathbuf, fullrep) :
  2389. X#ifndef SYSV
  2390. X                    (op & SYMLINK) ?
  2391. X                        symlink((p->r_flags & R_ONEDIRLINK) ? fstart : pathbuf,
  2392. X                            fullrep) :
  2393. X#endif
  2394. X#endif
  2395. X                    p->r_flags & R_ISX ?
  2396. X                        copymove(p) :
  2397. X                    /* move */
  2398. X                        rename(pathbuf, fullrep)
  2399. X                ) {
  2400. X                    fprintf(stderr,
  2401. X                        "%s -> %s has failed.\n", pathbuf, fullrep);
  2402. X                    printaliased = snap(first, p);
  2403. X                }
  2404. X            }
  2405. X            if (verbose || noex) {
  2406. X                if (p->r_flags & R_ISALIASED && !printaliased)
  2407. X                    strcpy(fstart, p->r_ffrom->fi_name);
  2408. X                fprintf(outfile, "%s %c%c %s%s%s\n",
  2409. X                    pathbuf,
  2410. X                    p->r_flags & R_ISALIASED ? '=' : '-',
  2411. X                    p->r_flags & R_ISCYCLE ? '^' : '>',
  2412. X                    fullrep,
  2413. X                    (p->r_fdel != NULL && !(op & APPEND)) ? " (*)" : "",
  2414. X                    noex ? "" : " : done");
  2415. X            }
  2416. X        }
  2417. X        printaliased = 0;
  2418. X    }
  2419. X    if (k != nreps)
  2420. X        fprintf(stderr, "Strange, did %d reps; %d were expected.\n",
  2421. X            k, nreps);
  2422. X    if (k == 0)
  2423. X        fprintf(stderr, "Nothing done.\n");
  2424. X}
  2425. X
  2426. X
  2427. Xstatic long appendalias(first, p, pprintaliased)
  2428. X    REP *first, *p;
  2429. X    int *pprintaliased;
  2430. X{
  2431. X    long ret;
  2432. X
  2433. X#ifdef MSDOS
  2434. X    int fd;
  2435. X
  2436. X    if ((fd = open(fullrep, O_RDONLY | O_BINARY, 0)) < 0) {
  2437. X        fprintf(stderr, "stat on %s has failed.\n", fullrep);
  2438. X        *pprintaliased = snap(first, p);
  2439. X    }
  2440. X    else {
  2441. X        ret = filelength(fd);
  2442. X        close(fd);
  2443. X    }
  2444. X#else
  2445. X    struct stat fstat;
  2446. X
  2447. X    if (stat(fullrep, &fstat)) {
  2448. X        fprintf(stderr, "append cycle stat on %s has failed.\n", fullrep);
  2449. X        *pprintaliased = snap(first, p);
  2450. X    }
  2451. X    else
  2452. X        ret = fstat.st_size;
  2453. X#endif
  2454. X
  2455. X    return(ret);
  2456. X}
  2457. X
  2458. X
  2459. END_OF_FILE
  2460. if test 49841 -ne `wc -c <'mmv.c.1'`; then
  2461.     echo shar: \"'mmv.c.1'\" unpacked with wrong size!
  2462. fi
  2463. # end of 'mmv.c.1'
  2464. fi
  2465. echo shar: End of archive 2 \(of 2\).
  2466. cp /dev/null ark2isdone
  2467. MISSING=""
  2468. for I in 1 2 ; do
  2469.     if test ! -f ark${I}isdone ; then
  2470.     MISSING="${MISSING} ${I}"
  2471.     fi
  2472. done
  2473. if test "${MISSING}" = "" ; then
  2474.     echo You have unpacked both archives.
  2475.     rm -f ark[1-9]isdone
  2476. else
  2477.     echo You still need to unpack the following archives:
  2478.     echo "        " ${MISSING}
  2479. fi
  2480. ##  End of shell archive.
  2481. exit 0
  2482. exit 0 # Just in case...
  2483.